﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SortableListExtensions.cs" company="TechBits">
//   Copyright (c) TechBits. All rights reserved.
// </copyright>
// <author>
//   Leandro G. Vacirca
// </author>
// <summary>
//   Defines the SortableList type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace IntelliShortCut.Helpers
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;

    /// <summary>
    /// Define metodos de extension para convertir listas a listas ordenables.
    /// </summary>
    public static class SortableListExtensions
    {
        #region "Atributos Publicos"

        /// <summary>
        /// Tipos de comparacion.
        /// </summary>
        public enum ComparerTypeEnum
        {
            /// <summary>
            /// Value = 0.
            /// </summary>
            LessThan = 0,

            /// <summary>
            /// Value = 1.
            /// </summary>
            EqualsTo = 1,

            /// <summary>
            /// Value = 2.
            /// </summary>
            GreaterThan = 2            
        }

        #endregion

        #region "Metodos Publicos"

        /// <summary>
        /// Convierte una lista en ordenable.
        /// </summary>
        /// <param name="source">La lista fuente de datos que se requiere ordenable.</param>
        /// <returns>
        /// Una lista que admite ordenamiento.
        /// </returns>
        public static IBindingList ToSortableList(this IList source)
        {
            var elementType = source.GetType().GetGenericArguments()[0];
            var type = typeof(SortableList<>).MakeGenericType(elementType);
            var wrapped = (IBindingList)Activator.CreateInstance(type, source);
            return wrapped;
        }

        /// <summary>
        /// Filtra una lista de objetos de tipo T en base a una tupla NombrePropiedad, ValorPropiedad y la devuelve ordenable.
        /// </summary>
        /// <param name="source">La lista fuente de datos que se requiere ordenable y filtrable.</param>
        /// <param name="comparerType">El tipo de comparador a aplicar a la tupla NombrePropiedad-ValorPropiedad.</param>
        /// <param name="propertyName">El nombre de la propiedad.</param>
        /// <param name="value">El valor de comparacion.</param>
        /// <typeparam name="T">El tipo de datos de los elementos de la lista.</typeparam>
        /// <typeparam name="K">El tipo de datos de la propiedad que se desea comparar.</typeparam>
        /// <returns>
        /// Lista de objetos de tipo T filtrada y ordenable; Null en caso que el valor de comparacion sea null.
        /// </returns>
        public static IBindingList Filter<T, K>(this IList<T> source, ComparerTypeEnum comparerType, string propertyName, K value)
        {
            try
            {
                return value.Equals(null)
                           ? null
                           : source.Where(
                                 s =>
                                 CompareValues(comparerType, Comparer.Default.Compare(s.GetType().GetProperty(propertyName).GetValue(s, null), Convert.ChangeType(value, s.GetType().GetProperty(propertyName).PropertyType))))
                                 .ToList().ToSortableList();
            }
            catch (Exception)
            {
                return null;
            }
        }

        #endregion

        #region "Metodos Privados"

        /// <summary>
        /// Aplica un comparador al resultado de un default comparer.
        /// </summary>
        /// <param name="comparerType">El tipo de comparador.</param>
        /// <param name="value">El resultado del default comparer.</param>
        /// <returns>
        /// True: si el resultado del comparador aplicado es true; false en caso contrario.
        /// </returns>
        private static bool CompareValues(ComparerTypeEnum comparerType, int value)
        {
            switch (comparerType)
            {
                case ComparerTypeEnum.LessThan:
                    {
                        return value < 0;
                    }
                
                case ComparerTypeEnum.EqualsTo:
                    {
                        return value == 0;
                    }
                
                case ComparerTypeEnum.GreaterThan:
                    {
                        return value > 0;
                    }
                
                default:
                    return false;
            }
        }

        #endregion
    }
}